package com.vickyleu.audiorecorder

import android.Manifest
import android.content.pm.PackageManager
import android.media.AudioFormat
import android.media.AudioRecord
import android.media.MediaRecorder
import android.os.Environment
import android.os.Handler
import android.util.Log
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import java.io.*
import java.util.*
import kotlin.math.abs
import kotlin.math.log10

/**
 * AudioRecorderPlugin
 */
class AudioRecorderPlugin : FlutterPlugin, ActivityAware, MethodCallHandler {

    private var activityAware: ActivityPluginBinding? = null
    private var channel: MethodChannel? = null


    private var isRecording = false
    private var mRecorder: AudioRecord? = null

    private var mFilePath: String? = null
    private var startTime: Date? = null
    private var mExtension: String? = ""
    private var wavRecorder: WavRecorder? = null

    private var dos: DataOutputStream? = null
    private var amplitude = 0
    var bufferSize = 0
    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        when (call.method) {
            "convertMp3" -> {
                val pcm = call.argument<String>("pcm")
                val outPath = call.argument<String>("path")
                encodeToMp3(pcm, outPath, result)
            }
            "start" -> {
                Log.d(LOG_TAG, "Start")
                val path = call.argument<String>("path")
                mExtension = call.argument("extension")
                startTime = Calendar.getInstance().time
                mFilePath = if (path != null) {
                    path
                } else {
                    val fileName = startTime!!.time.toString()
                    Environment.getExternalStorageDirectory().absolutePath + "/" + fileName + mExtension
                }
                Log.e("mFilePath", mFilePath!!)
                startRecording()
                result.success(null)
            }
            "stop" -> {
                Log.d(LOG_TAG, "Stop")
                stopRecording()
                if (mFilePath == null) {
                    Log.e(LOG_TAG, "mFilePath==null")
                    result.success(null)
                    startTime = null
                    mFilePath = null
                    return
                }
                val file = File(mFilePath)
                if (!file.exists()) {
                    Log.e(LOG_TAG, "!file.exists()")
                    result.success(null)
                    startTime = null
                    mFilePath = null
                    return
                }
                val duration = Calendar.getInstance().time.time - (startTime?.time?:0)
                Log.d(LOG_TAG, "Duration : $duration")
                val recordingResult = HashMap<String, Any?>()
                recordingResult["duration"] = duration
                recordingResult["path"] = mFilePath
                recordingResult["audioOutputFormat"] = mExtension
                result.success(recordingResult)
                startTime = null
                mFilePath = null
            }
            "isRecording" -> {
                Log.d(LOG_TAG, "Get isRecording")
                result.success(isRecording)
            }
            "hasPermissions" -> {
                Log.d(LOG_TAG, "Get hasPermissions")
                val context = activityAware?.activity
                if (context != null) {
                    val pm = context.packageManager
                    val hasStoragePerm = pm.checkPermission(
                        Manifest.permission.WRITE_EXTERNAL_STORAGE,
                        context.packageName
                    )
                    val hasRecordPerm =
                        pm.checkPermission(Manifest.permission.RECORD_AUDIO, context.packageName)
                    val hasPermissions =
                        hasStoragePerm == PackageManager.PERMISSION_GRANTED && hasRecordPerm == PackageManager.PERMISSION_GRANTED
                    result.success(hasPermissions)
                } else {
                    result.success(false)
                }
            }
            else -> result.notImplemented()
        }
    }

    private fun startRecording() {
        if (isOutputFormatWav) {
            startWavRecording()
        } else {
            startNormalRecording()
        }
        Log.e("startRecording", "startRecording: $isRecording")
        if (isRecording) {
            mHandler.postDelayed(mUpdateMicStatusTimer, 50)
        }
    }

    private fun startNormalRecording() {
        try {
            val audioSource = MediaRecorder.AudioSource.MIC
            val sampleRateInHz = 44100
            val channelConfig = AudioFormat.CHANNEL_IN_MONO //单声道
            val audioFormat = AudioFormat.ENCODING_PCM_16BIT //量化位数
            bufferSize =
                AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat) //计算最小缓冲区
            mRecorder =
                AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSize)
            val recordPath = File(mFilePath)
            if (recordPath.exists()) {
                recordPath.delete()
            }
            if (!recordPath.parentFile.isDirectory) {
                recordPath.parentFile.delete()
            }
            if (!recordPath.parentFile.exists()) {
                recordPath.parentFile.mkdirs()
            }
            recordPath.createNewFile()
            dos = DataOutputStream(BufferedOutputStream(FileOutputStream(mFilePath)))
            mRecorder!!.startRecording() //开始录音
            isRecording = true
            Thread {
                var r = 0
                val buffer = ByteArray(bufferSize)
                while (isRecording) {
                    val bufferReadResult = mRecorder!!.read(buffer, 0, bufferSize)
                    amplitude = buffer[0].toInt() and 0xff shl 8 or buffer[1].toInt()
                    for (i in 0 until bufferReadResult) {
                        try {
                            dos?.write(buffer[i].toInt())
                        } catch (e: IOException) {
                            e.printStackTrace()
                        }
                    }
                    r++
                }
            }.start()
        } catch (e2: Exception) {
        }
    }

    private fun startWavRecording() {
        try {
            wavRecorder = WavRecorder(activityAware?.activity ?: return, mFilePath)
            wavRecorder!!.startRecording()
            isRecording = true
        } catch (e: Exception) {
        }
    }

    private fun stopRecording() {
        mHandler.removeCallbacksAndMessages(null)
        if (isRecording) {
            if (isOutputFormatWav) {
                stopWavRecording()
            } else {
                stopNormalRecording()
            }
        }
    }

    private fun stopNormalRecording() {
        if (mRecorder != null) {
            try {
                if (isRecording) {
                    mRecorder!!.stop()
                    dos!!.close()
                }
            } catch (e: Exception) {
            }
            try {
                mRecorder!!.release()
            } catch (e: Exception) {
            }
            mRecorder = null
        }
        isRecording = false
    }

    private fun encodeToMp3(inPcmPath: String?, outPath: String?, result: MethodChannel.Result) {
        val pcm = File(inPcmPath)
        var flag = 0
        if (pcm.exists()) {
            val output = File(outPath)
            try {
                if (output.exists()) {
                    output.delete()
                }
                output.createNewFile()
                MP3Recorder.init(44100, 1, 0, 44100, 16, 2)
                MP3Recorder.convertMp3(inPcmPath, outPath)
                flag = -1
            } catch (e: IOException) {
                e.printStackTrace()
            }
        }
        if (flag == -1) {
            result.success(outPath)
        } else {
            result.error(flag.toString(), "转换失败", null)
        }
    }

    private val mHandler = Handler()
    private val mUpdateMicStatusTimer = Runnable { updateMicStatus() }
    private fun updateMicStatus() {
        // Determine amplitude
        val ratio = 7.0 * log10(abs(amplitude) / 32768.0)
        var level = 0
        level = if (ratio > 0.1 && ratio < 0.2) {
            2
        } else if (ratio > 0.2 && ratio < 0.3) {
            3
        } else if (ratio > 0.3 && ratio < 0.4) {
            4
        } else if (ratio > 0.4 && ratio < 0.5) {
            5
        } else if (ratio > 0.5 && ratio < 0.6) {
            6
        } else if (ratio > 0.6 && ratio < 0.7) {
            7
        } else if (ratio > 0.7) {
            7
        } else {
            1
        }
        val map: MutableMap<Any, Any> = HashMap()
        map["amplitude"] = level
        callFlutter("onAmplitude", map)
        val duration: Double = System.currentTimeMillis() - (startTime?.time?.toDouble() ?: 0.0)
        val map2: MutableMap<Any, Any> = HashMap()
        map2["duration"] = duration
        callFlutter("onDuration", map2)
        if (activityAware?.activity != null) {
            mHandler.postDelayed(mUpdateMicStatusTimer, 50)
        }
        /*
         * if (db > 1) { vuSize = (int) (20 * Math.log10(db)); Log.i("mic_",
         * "麦克风的音量的大小：" + vuSize); } else Log.i("mic_", "麦克风的音量的大小：" + 0);
         */
    }

    private fun callFlutter(method: String, map: Map<*, *>) {
        activityAware?.activity?.runOnUiThread {
            channel?.invokeMethod(method, map)
        }
    }

    private fun stopWavRecording() {
        try {
            wavRecorder?.stopRecording()
        } catch (e: Exception) {
        }
        isRecording = false
    }

    private fun getOutputFormatFromString(outputFormat: String): Int {
        return when (outputFormat) {
            ".mp4", ".aac", ".m4a" -> MediaRecorder.OutputFormat.MPEG_4
            else -> MediaRecorder.OutputFormat.AMR_NB
        }
    }

    private val isOutputFormatWav: Boolean
        get() = mExtension == ".wav"

    companion object {
        private const val LOG_TAG = "AudioRecorder"
    }

    override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        channel = MethodChannel(flutterPluginBinding.binaryMessenger, "audio_recorder")
        channel?.setMethodCallHandler(this)
    }

    override fun onDetachedFromEngine(plugin: FlutterPlugin.FlutterPluginBinding) {
        channel?.setMethodCallHandler(null)
    }


    override fun onAttachedToActivity(plugin: ActivityPluginBinding) {
        this.activityAware = plugin
    }

    override fun onDetachedFromActivityForConfigChanges() {
        onDetachedFromActivity()
    }

    override fun onDetachedFromActivity() {
        activityAware = null
    }

    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
        onAttachedToActivity(binding)
    }
}